package com.itheima.admin.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.itheima.admin.gateway.util.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 管理端网关认证过滤器
 * 功能：
 * 1. 验证JWT token有效性
 * 2. 检查token黑名单
 * 3. 验证用户类型（必须是admin）
 * 4. 将用户信息添加到请求头
 */
@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String TOKEN_BLACKLIST_PREFIX = "auth:token:blacklist:";
    
    // 白名单路径：不需要token验证的接口
    private static final String[] WHITE_LIST = {
            "/auth/api/v1/auth/login",           // 登录接口
            "/auth/api/v1/wechat/auth-url",      // 微信授权URL
            "/auth/api/v1/wechat/callback",      // 微信回调
            "/auth/api/v1/wechat/bind"           // 微信绑定
    };
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        
        String path = request.getURI().getPath();
        log.info("管理端网关拦截请求：{}", path);
        
        // 1. 白名单路径直接放行
        if (isWhiteListPath(path)) {
            log.info("白名单路径，直接放行：{}", path);
            return chain.filter(exchange);
        }
        
        // 2. 获取token
        String token = getToken(request);
        if (token == null || token.trim().isEmpty()) {
            log.warn("请求未携带token：{}", path);
            return unauthorized(response, "请先登录");
        }
        
        try {
            // 3. 验证token格式和签名
            if (!JwtUtil.validateToken(token)) {
                log.warn("Token验证失败：{}", token);
                return unauthorized(response, "Token无效或已过期");
            }
            
            // 4. 检查token黑名单
            String blacklistKey = TOKEN_BLACKLIST_PREFIX + token;
            if (Boolean.TRUE.equals(redisTemplate.hasKey(blacklistKey))) {
                log.warn("Token已在黑名单中：{}", token);
                return unauthorized(response, "Token已失效，请重新登录");
            }
            
            // 5. 解析token获取用户信息
            Claims claims = JwtUtil.getClaims(token);
            Integer userId = (Integer) claims.get("id");
            String userType = (String) claims.get("userType");
            String username = (String) claims.get("username");
            
            // 6. 验证用户类型（管理端必须是admin）
            if (!"admin".equalsIgnoreCase(userType)) {
                log.warn("非管理员用户尝试访问管理端：userId={}, userType={}", userId, userType);
                return forbidden(response, "权限不足");
            }
            
            // 7. 将用户信息添加到请求头，传递给下游服务
            ServerHttpRequest modifiedRequest = request.mutate()
                    .header("X-User-Id", String.valueOf(userId))
                    .header("X-User-Type", userType)
                    .header("X-Username", username)
                    .build();
            
            log.info("Token验证通过，用户：{} (ID:{})", username, userId);
            
            // 8. 放行请求
            return chain.filter(exchange.mutate().request(modifiedRequest).build());
            
        } catch (Exception e) {
            log.error("Token验证异常", e);
            return unauthorized(response, "Token验证失败");
        }
    }
    
    /**
     * 判断是否为白名单路径
     */
    private boolean isWhiteListPath(String path) {
        for (String whiteListPath : WHITE_LIST) {
            if (path.startsWith(whiteListPath)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 从请求中获取token
     * 支持两种方式：
     * 1. Header: Authorization: Bearer {token}
     * 2. Query: ?token={token}
     */
    private String getToken(ServerHttpRequest request) {
        // 1. 从Header获取
        String authorization = request.getHeaders().getFirst("Authorization");
        if (authorization != null && !authorization.trim().isEmpty() && authorization.startsWith("Bearer ")) {
            return authorization.substring(7);
        }
        
        // 2. 从Query参数获取
        String token = request.getQueryParams().getFirst("token");
        if (token != null && !token.trim().isEmpty()) {
            return token;
        }
        
        return null;
    }
    
    /**
     * 返回401未授权
     */
    private Mono<Void> unauthorized(ServerHttpResponse response, String message) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return writeResponse(response, 401, message);
    }
    
    /**
     * 返回403禁止访问
     */
    private Mono<Void> forbidden(ServerHttpResponse response, String message) {
        response.setStatusCode(HttpStatus.FORBIDDEN);
        return writeResponse(response, 403, message);
    }
    
    /**
     * 写入响应
     */
    private Mono<Void> writeResponse(ServerHttpResponse response, int code, String message) {
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        
        Map<String, Object> result = new HashMap<>();
        result.put("code", code);
        result.put("errorMessage", message);
        result.put("data", null);
        
        String jsonString = JSON.toJSONString(result);
        DataBuffer buffer = response.bufferFactory().wrap(jsonString.getBytes(StandardCharsets.UTF_8));
        
        return response.writeWith(Mono.just(buffer));
    }
    
    @Override
    public int getOrder() {
        return -100; // 优先级：数字越小优先级越高
    }
}

